package cn.sdack.go.users.aspect;

import cn.sdack.go.common.dtos.BaseDTO;
import cn.sdack.go.common.entities.PermissionEntity;
import cn.sdack.go.common.querys.BaseQuery;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 权限检查类
 * 需引入 common 模块使用
 * @author sdack
 * @date 2024/2/8
 */
@Aspect
@Component
public class PermissionsAspect {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 数据取出之前检查
     * @param jp
     */
    @Before("@annotation(cn.sdack.go.common.annotation.BeforeAuthentication)")
    public void checkBefore(JoinPoint jp) {
        Object[] args = jp.getArgs();
        if (args.length == 0) {
            throw new RuntimeException("Unable authenticate");
        }
        JwtAuthenticationToken authentication = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        long gid = 0;
        List<Long> agent;
        String role = "";
        try {
            gid = Long.parseLong(authentication.getToken().getClaims().get("gid").toString());
            agent = (List<Long>) authentication.getToken().getClaims().get("agent");
            role = authentication.getToken().getClaims().get("role").toString();
        } catch (Exception e) {
            throw new RuntimeException("Unable authenticate");
        }
        Object arg0 = args[0];
        if (!"super".equals(role)) {
            if (agent == null) {
                throw new RuntimeException("Unable authenticate");
            }
            if (arg0 instanceof PermissionEntity) {
                PermissionEntity base = (PermissionEntity) arg0;
                if (base.getGid() == null) {
                    base.setGid(gid);
                }
                if (base.getGid() < 1) {
                    base.setGid(gid);
                    base.setGids(new ArrayList<>());
                    base.getGids().addAll(agent);
                } else {
                    Optional<Long> optional = agent.stream().filter(it -> base.getGid() == it.longValue()).findFirst();
                    if (optional.isEmpty()) {
                        throw new RuntimeException("Unrecognized organization");
                    }
                }
            } else if (arg0 instanceof BaseQuery) {
                BaseQuery base = (BaseQuery) arg0;
                base.setGids(new ArrayList<>());
                base.getGids().addAll(agent);
                if (base.getGid() == null) {
                    base.setGid(gid);
                }
                if (base.getGid() < 1) {
                    base.setGid(gid);
                    base.getGids().add(gid);
                }
            } else if (arg0 instanceof BaseDTO) {
                BaseDTO base = (BaseDTO) arg0;
                base.setGids(new ArrayList<>());
                base.getGids().addAll(agent);
                if (base.getGid() == null) {
                    base.setGid(gid);
                }
                if (base.getGid() < 1) {
                    base.setGid(gid);
                    base.getGids().add(gid);
                } else {
                    Optional<Long> optional = agent.stream().filter(it -> base.getGid() == it.longValue()).findFirst();
                    if (optional.isEmpty()) {
                        throw new RuntimeException("Unrecognized organization");
                    }
                }
            }
            else if (arg0 instanceof Long) {
                Optional<Long> optional = agent.stream().filter(it -> (Long) arg0 == it.longValue()).findFirst();
                if (optional.isEmpty()) {
                    throw new RuntimeException("Unrecognized organization");
                }
            } else {
                throw new RuntimeException("Unable authenticate");
            }
        }
    }

    /**
     * 在得到数据后
     * @param base
     */
    @AfterReturning(value = "@annotation(cn.sdack.go.common.annotation.AfterAuthentication)", returning = "base")
    public void checkAfter(PermissionEntity base) {
        if (base == null) {
            throw new RuntimeException("non-existent");
        }
        JwtAuthenticationToken authentication = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        List<Long> agent;
        String role = "";
        try {
            agent = (List<Long>) authentication.getToken().getClaims().get("agent");
            role = authentication.getToken().getClaims().get("role").toString();
        } catch (Exception e) {
            throw new RuntimeException("Unable authenticate");
        }

        if (!"super".equals(role)) {
            if (agent == null) {
                throw new RuntimeException("Unable authenticate");
            }
            Optional<Long> optional = agent.stream().filter(it -> base.getGid() == it.longValue()).findFirst();
            if (optional.isEmpty()) {
                throw new RuntimeException("Unrecognized organization");
            }
        }
    }

}
